#include "include/EventLoop.hpp"
#include "include/TcpConnection.hpp"
#include <iostream>
#include <stdio.h>
using std::cout;
using std::endl;
int EventLoop::createEpfd(){
    int ret = epoll_create(1);
    if(ret<0){
        perror("epoll_create");
    }
    return ret;
}

int EventLoop::createEventfd(){
    int ret = ::eventfd(0,0);
    if(ret < 0){
        perror("eventfd");
    }
    return ret;
}

void EventLoop::wakeup(){
    cout<<"compute thread "<<pthread_self()<<" wake up io thread."<<endl;
    eventfd_t val = 1;
    int ret = write(_eventfd,&val,sizeof(val));
    if(ret != sizeof(val)){
        perror("write");
    }
}

void EventLoop::runInLoop(Functor && f){
    {
        unique_lock<mutex> u_lock(_mutex);
        _functors.push_back(std::move(f));
    }
    wakeup();
}

void EventLoop::handleReadEvent(){
    eventfd_t val = 0;
    int ret = ::read(_eventfd,&val,sizeof(val));
    if(ret != sizeof(val)){
        perror("read");
        
    }
}

void EventLoop::doFunctors(){
    cout<<"IO thread "<<pthread_self()<<" doFunctors "<<endl;
    vector<Functor> temp_functors; 
    {
        unique_lock<mutex> u_lock(_mutex);
        temp_functors.swap(_functors);
    }

    for(auto & elem: temp_functors){
        elem();
    }
}


void EventLoop::waitEpollFd(){
    int readyNum = epoll_wait(_epfd,evArr.data(),evArr.size(),50000);
    for(int i =0 ;i<readyNum ;++i){
        if(evArr[i].data.fd == _acceptor.fd()){
            handleNewConnect();
        }else if(evArr[i].data.fd == _eventfd){
            handleReadEvent();
            doFunctors();
        }else{
            handleMessage(evArr[i].data.fd);
        }
    }
    if(readyNum == 0){
        cout<<"epoll time out."<<endl;
    }
    if(readyNum == -1&&errno == EINTR){
        return;
    }else if(readyNum == -1){
        perror("epoll_wait");
        exit(EXIT_FAILURE);
    }
}

void EventLoop::addEpollReadEvent(int fd){
    epoll_event epevent;
    memset(&epevent,0,sizeof(epevent));
    epevent.events=EPOLLIN;
    epevent.data.fd = fd;
    int ret = epoll_ctl(_epfd,EPOLL_CTL_ADD,fd,&epevent);
    if(ret < 0){
        perror("epoll_ctl::add");
    }
}

void EventLoop::delEpollReadEvent(int fd){
    epoll_event ev;
    memset(&ev,0,sizeof(ev));
    ev.data.fd = fd;
    ev.events = EPOLLIN;
    int ret = epoll_ctl(_epfd,EPOLL_CTL_DEL,fd,&ev);
    if(ret<0){
        perror("epoll_ctl::del");
    }
}
void EventLoop::handleNewConnect(){
    int fd = _acceptor.accept();
    tcpMap[fd] = std::make_shared<TcpConnection>(fd, this);
    addEpollReadEvent(fd);
    tcpMap[fd]->setFunctionsCall(_onConnect,_onMessage,_onClose);
    tcpMap[fd]->handleNewConnectCallBack();
}

void EventLoop::handleMessage(int fd){
    if(tcpMap.count(fd)!=0){
        auto elem = tcpMap[fd];
        if(elem->isClosed()){
            elem->handleCloseCallBack();
            delEpollReadEvent(fd);
            tcpMap.erase(fd);
            return;
        }
        elem->handleMessageCallBack();
    }
}

void EventLoop::loop(){
    isLoop = true;
    while(isLoop){
        waitEpollFd();
    }
}


